﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Printing;
using System.Windows.Shapes;
 
namespace WellDone
{
    public static class Globals
    {
        // Table Type 
        public enum TableType
        {
            Base = 0,
            Ractangle = 1,
            Ellipse = 2
        }

        // Integer Constants
        public const int k_int_days_in_week = 7;
        public const int k_int_margin_thickness = 100;
        public const int k_int_period_editor_height = 560;
        public const int k_int_schedule_edit_width_offset = 10;
        public const int k_int_schedule_edit_height_offset = 155;
        public const int k_int_schedule_wrap_chars = 20;
        public const int k_int_scrollbar_width = 20;
        public const long k_long_max_ticks_between_clicks = 3000000;
        
        // String Constants
        public const string k_str_cannot_connect = "Unable to connect to server";
        public const string k_str_invalid_login = "Invalid Username/Password";
        public const string k_str_reports_url_key = "ReportsURL";

        // Misc. Constants
        public const DayOfWeek k_dow_week_start = DayOfWeek.Sunday;
        
        // Misc. Data
        public static string username;
        public static int loginID;
        public static int token;
        public static int menuItemID;
        public static DateTime dtScheduleStart = DateTime.Now.WeekStart(Globals.k_dow_week_start);
        public static ServiceReferenceOpenRPOS.Employee currentEmployee;
        public static ServiceReferenceOpenRPOS.Login currentLogin;
        public static ServiceReferenceOpenRPOS.Employee selectedEmployee;
        public static ServiceReferenceOpenRPOS.Login selectedLogin;
        public static Regex reDigits = new Regex(@"[^\d]");
        // Create storage to track employees that are also managers
        public static List<int> lstManagers = new List<int>();
        
        // Root display
        public static ScrollViewer root;

        // Screens
        public static Account account = new Account();
        public static Employees employees = new Employees();
        public static Login login = new Login();
        public static MainPage mainPage = new MainPage();
        public static MenuItems menuItems = new MenuItems();
        public static Reports reports = new Reports();
        public static Schedule schedule = new Schedule();
        public static TimeSheet timeSheet = new TimeSheet();
        public static FloorLayout floorLayout = new FloorLayout();
        public static Configuration configuration = new Configuration();

        // Database interface class
        public static DatabaseInterface db = new DatabaseInterface();


        public static void Init()
        {
            // Initialize the display area
            root = new ScrollViewer();
            root.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
            root.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        }

        public static void cannotConnect()
        {
            login.titleMessage.Visibility = Visibility.Visible;
            login.titleMessage.Content = Globals.k_str_cannot_connect;
            // Go to login screen
            root.Content = Globals.login;
        }

        public static void showWaitCursor(bool wait)
        {
            Globals.mainPage.WaitCursor.Visibility = (wait) ? Visibility.Visible : Visibility.Collapsed;
            Globals.mainPage.WaitCursor.Width = (wait) ? Globals.mainPage.ContentBorder.ActualWidth : 0;
            Globals.mainPage.WaitCursor.Height = (wait) ? Globals.mainPage.ContentBorder.ActualHeight : 0;
            Globals.mainPage.WaitCursor.Cursor = (wait) ? Cursors.Wait : Cursors.Arrow;
            Globals.mainPage.hbAccount.IsEnabled = !wait;
            Globals.mainPage.hbEmployees.IsEnabled = !wait;
            Globals.mainPage.hbMenuItems.IsEnabled = !wait;
            Globals.mainPage.hbReports.IsEnabled = !wait;
            Globals.mainPage.hbSchedule.IsEnabled = !wait;
            Globals.mainPage.hbTimeSheet.IsEnabled = !wait;
            Globals.mainPage.hbFloorLayout.IsEnabled = !wait;
        }

        public static void Print(this FrameworkElement element, string Document, bool PrintLandscape)
        {
            Print(new List<FrameworkElement>() { element }, Document, HorizontalAlignment.Center, VerticalAlignment.Top, new Thickness(k_int_margin_thickness), PrintLandscape, true, null);
        }

        // The following Print method was adapted from source code originally written by Alishah Novin, 1 Dec 2010
        // Link to article: http://www.codeproject.com/Articles/132431/Simplified-UI-Printing-in-Silverlight
        // License: http://www.codeproject.com/info/cpol10.aspx
        private static void Print<T>(this List<T> elements, string Document, HorizontalAlignment HorizontalAlignment, VerticalAlignment VerticalAlignment, Thickness PageMargin, bool PrintLandscape, bool ShrinkToFit, Action OnPrintComplete)
        {
            PrintDocument printDocument = new PrintDocument();
            Document = (string.IsNullOrEmpty(Document)) ? "Print Document" : Document;
            int currentItemIndex = 0;
            printDocument.PrintPage += delegate(object sender, PrintPageEventArgs evt)
            {
                if (!typeof(FrameworkElement).IsAssignableFrom(elements[currentItemIndex].GetType()))
                {
                    throw new Exception("Element must be an object inheriting from FrameworkElement");
                }

                FrameworkElement element = elements[currentItemIndex] as FrameworkElement;

                if (element.Parent == null || element.ActualWidth == double.NaN || element.ActualHeight == double.NaN)
                {
                    throw new Exception("Element must be rendered, and must have a parent in order to print.");
                }

                TransformGroup transformGroup = new TransformGroup();

                //First move to middle of page...
                transformGroup.Children.Add(new TranslateTransform() { X = (evt.PrintableArea.Width - element.ActualWidth) / 2, Y = (evt.PrintableArea.Height - element.ActualHeight) / 2 });
                double scale = 1;
                if (PrintLandscape)
                {
                    //Then, rotate around the center
                    transformGroup.Children.Add(new RotateTransform() { Angle = 90, CenterX = evt.PrintableArea.Width / 2, CenterY = evt.PrintableArea.Height / 2 });

                    if (ShrinkToFit)
                    {
                        if ((element.ActualWidth + PageMargin.Left + PageMargin.Right) > evt.PrintableArea.Height)
                        {
                            scale = Math.Round(evt.PrintableArea.Height / (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
                        }
                        if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > evt.PrintableArea.Width)
                        {
                            double scale2 = Math.Round(evt.PrintableArea.Width / (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
                            scale = (scale2 < scale) ? scale2 : scale;
                        }
                    }
                }
                else if (ShrinkToFit)
                {
                    //Scale down to fit the page + margin

                    if ((element.ActualWidth + PageMargin.Left + PageMargin.Right) > evt.PrintableArea.Width)
                    {
                        scale = Math.Round(evt.PrintableArea.Width / (element.ActualWidth + PageMargin.Left + PageMargin.Right), 2);
                    }
                    if ((element.ActualHeight + PageMargin.Top + PageMargin.Bottom) > evt.PrintableArea.Height)
                    {
                        double scale2 = Math.Round(evt.PrintableArea.Height / (element.ActualHeight + PageMargin.Top + PageMargin.Bottom), 2);
                        scale = (scale2 < scale) ? scale2 : scale;
                    }
                }

                //Scale down to fit the page + margin
                if (scale != 1)
                {
                    transformGroup.Children.Add(new ScaleTransform() { ScaleX = scale, ScaleY = scale, CenterX = evt.PrintableArea.Width / 2, CenterY = evt.PrintableArea.Height / 2 });
                }

                if (VerticalAlignment == VerticalAlignment.Top)
                {
                    //Now move to Top
                    if (PrintLandscape)
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = PageMargin.Top - (evt.PrintableArea.Height - (element.ActualWidth * scale)) / 2 });
                    }
                    else
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = PageMargin.Top - (evt.PrintableArea.Height - (element.ActualHeight * scale)) / 2 });
                    }
                }
                else if (VerticalAlignment == VerticalAlignment.Bottom)
                {
                    //Now move to Bottom
                    if (PrintLandscape)
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = ((evt.PrintableArea.Height - (element.ActualWidth * scale)) / 2) - PageMargin.Bottom });
                    }
                    else
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = ((evt.PrintableArea.Height - (element.ActualHeight * scale)) / 2) - PageMargin.Bottom });
                    }
                }

                if (HorizontalAlignment == HorizontalAlignment.Left)
                {
                    //Now move to Left
                    if (PrintLandscape)
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = PageMargin.Left - (evt.PrintableArea.Width - (element.ActualHeight * scale)) / 2, Y = 0 });
                    }
                    else
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = PageMargin.Left - (evt.PrintableArea.Width - (element.ActualWidth * scale)) / 2, Y = 0 });
                    }
                }
                else if (HorizontalAlignment == HorizontalAlignment.Right)
                {
                    //Now move to Right
                    if (PrintLandscape)
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = ((evt.PrintableArea.Width - (element.ActualHeight * scale)) / 2) - PageMargin.Right, Y = 0 });
                    }
                    else
                    {
                        transformGroup.Children.Add(new TranslateTransform() { X = ((evt.PrintableArea.Width - (element.ActualWidth * scale)) / 2) - PageMargin.Right, Y = 0 });
                    }
                }

                evt.PageVisual = element;
                evt.PageVisual.RenderTransform = transformGroup;

                //Increment to next item,
                currentItemIndex++;

                //If the currentItemIndex is less than the number of elements, keep printing
                evt.HasMorePages = currentItemIndex < elements.Count;
            };

            printDocument.EndPrint += delegate(object sender, EndPrintEventArgs evt)
            {
                foreach (var item in elements)
                {
                    FrameworkElement element = item as FrameworkElement;
                    //Reset everything...
                    TransformGroup transformGroup = new TransformGroup();
                    transformGroup.Children.Add(new ScaleTransform() { ScaleX = 1, ScaleY = 1 });
                    transformGroup.Children.Add(new RotateTransform() { Angle = 0 });
                    transformGroup.Children.Add(new TranslateTransform() { X = 0, Y = 0 });
                    element.RenderTransform = transformGroup;
                }

                //Callback to complete
                if (OnPrintComplete != null)
                {
                    OnPrintComplete();
                }
            };

            printDocument.Print(Document);
        }

    }

    // The following extension was adapted from http://omegacoder.com/?p=661
    public static class StringExtensions
    {
        /// <summary>
        /// Use this function like string.Split but instead of a character to split on,
        /// use a maximum line width size. This is similar to a Word Wrap where no words will be split.</summary>
        /// Note if the a word is longer than the maxcharactes it will be trimmed from the start.
        /// <param name="initial">The string to parse.</param>
        /// <param name="MaxCharacters">The maximum size.</param>
        /// <remarks>This function will remove some white space at the end of a line, but allow for a blank line.</remarks>
        ///
        /// <returns>An array of strings.</returns>
        public static string WordWrap(this string initial, int MaxCharacters)
        {
            string strReturn = "";
            if (string.IsNullOrEmpty(initial) == false)
            {
                string targetGroup = "Line";
                string theRegex = string.Format(@"(?<{0}>.{{1,{1}}})(?:\W|$)", targetGroup, MaxCharacters);
                MatchCollection matches = Regex.Matches(initial, theRegex, RegexOptions.IgnoreCase
                    | RegexOptions.Multiline
                    | RegexOptions.ExplicitCapture
                    | RegexOptions.CultureInvariant
                    );
                if (matches != null)
                    if (matches.Count > 0)
                        foreach (Match m in matches)
                            strReturn += m.Groups[targetGroup].Value + "\n";
            }            
            return strReturn.Trim();
        }
    }

    // The following code was adapted from code provided by Peter Mortensen
    // Link: http://stackoverflow.com/questions/38039/how-can-i-get-the-datetime-for-the-start-of-the-week
    public static class DateTimeExtensions
    {
        public static DateTime WeekStart(this DateTime dt, DayOfWeek weekStart)
        {
            int diff = dt.DayOfWeek - weekStart;
            if (diff < 0)
            {
                diff += Globals.k_int_days_in_week;
            }

            return dt.AddDays(-1 * diff).Date;
        }
    }


    public static class DecimalExtensions
    {
        public static void SetNumberTextBox(object sender, KeyEventArgs e)
        {
            const int KEYCODE_Dot_OnKeyboard = 190;
            const int KEYCODE_Dot_OnNumericKeyPad = 110;

            //Do not allow special characters
            if (Keyboard.Modifiers == ModifierKeys.Shift && !(e.Key == Key.Tab && (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift))
            {
                e.Handled = true;
            }

            //Only allow numbers and decimal
            if ((e.Key >= Key.D0 && e.Key <= Key.D9 || e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9 || e.Key == Key.Tab))
            {
                string strkey = e.Key.ToString().Substring(e.Key.ToString().Length - 1, e.Key.ToString().Length - (e.Key.ToString().Length - 1));

                if (e.Key >= Key.D0 && e.Key <= Key.D9 || e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)
                {
                    //Do not allow more than 2 digits after decimal point
                    TextBox tb = sender as TextBox;
                    int cursorPosLeft = tb.SelectionStart;
                    int cursorPosRight = tb.SelectionStart + tb.SelectionLength;
                    string result1 = tb.Text.Substring(0, cursorPosLeft) + strkey + tb.Text.Substring(cursorPosRight);
                    string[] parts = result1.Split('.');
                    if (parts.Length > 1)
                    {
                        if (parts[1].Length > 2 || parts.Length > 2)
                        {
                            e.Handled = true;
                        }
                    }
                }

                //Do not allow multiple decimal points
                if (((TextBox)sender).Text.Contains(".") && (e.PlatformKeyCode == KEYCODE_Dot_OnKeyboard || e.PlatformKeyCode == KEYCODE_Dot_OnNumericKeyPad))
                {
                    e.Handled = true;
                }
            }
            else
            {
                e.Handled = true;
            }

            //Do not allow alphabets and space
            if (e.Key >= Key.A && e.Key <= Key.Z || e.Key == Key.Space)
            {
                e.Handled = true;
            }
        }
        /// <summary>
        /// Set textbox to allow only decimal positive numbers 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public static void SetDecimalTextBox(object sender, KeyEventArgs e)
        {
            const int KEYCODE_Dot_OnKeyboard = 190;
            const int KEYCODE_Dot_OnNumericKeyPad = 110;

            //Do not allow special characters
            if (Keyboard.Modifiers == ModifierKeys.Shift && !(e.Key == Key.Tab && (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift))
            {
                e.Handled = true;
            }

            //Only allow numbers and decimal
            if ((e.Key >= Key.D0 && e.Key <= Key.D9 ||
                    e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9 || e.Key == Key.Tab ||
                    e.PlatformKeyCode == KEYCODE_Dot_OnKeyboard ||
                    e.PlatformKeyCode == KEYCODE_Dot_OnNumericKeyPad))
            {
                string strkey = e.Key.ToString().Substring(e.Key.ToString().Length - 1,
                        e.Key.ToString().Length - (e.Key.ToString().Length - 1));

                if (e.Key >= Key.D0 && e.Key <= Key.D9 ||
                    e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)
                {
                    //Do not allow more than 2 digits after decimal point
                    TextBox tb = sender as TextBox;
                    int cursorPosLeft = tb.SelectionStart;
                    int cursorPosRight = tb.SelectionStart + tb.SelectionLength;
                    string result1 = tb.Text.Substring(0, cursorPosLeft) +
                          strkey + tb.Text.Substring(cursorPosRight);
                    string[] parts = result1.Split('.');
                    if (parts.Length > 1)
                    {
                        if (parts[1].Length > 2 || parts.Length > 2)
                        {
                            e.Handled = true;
                        }
                    }
                }

                //Do not allow multiple decimal points
                if (((TextBox)sender).Text.Contains(".") && (e.PlatformKeyCode ==
                      KEYCODE_Dot_OnKeyboard ||
                      e.PlatformKeyCode == KEYCODE_Dot_OnNumericKeyPad))
                {
                    e.Handled = true;
                }
            }
            else
            {
                e.Handled = true;
            }

            //Do not allow alphabets and space
            if (e.Key >= Key.A && e.Key <= Key.Z || e.Key == Key.Space)
            {
                e.Handled = true;
            }
        }
    }

}
